﻿using System;
using System.Linq;

namespace Microsoft.CSA.FeedSync.FeedSyncLibrarySL
{
    public class FeedSyncFeed<T> where T : Microsoft.CSA.FeedSync.FeedSyncLibrarySL.FeedSyncItem, new()
    {
        protected System.Xml.Linq.XDocument m_XDocument;

        protected Microsoft.CSA.FeedSync.FeedSyncLibrarySL.Sharing m_Sharing;

        protected System.Collections.Generic.Dictionary<string, T> m_FeedSyncItemDictionary = new System.Collections.Generic.Dictionary<string, T>();

        protected System.Xml.Linq.XElement m_ItemContainerXElement = null;

        internal FeedSyncFeed()
        {
        }

        public FeedSyncFeed(string i_FeedContents, System.Xml.XmlQualifiedName i_ItemContainerXmlQualifiedName)
        {
            if (System.String.IsNullOrEmpty(i_FeedContents))
                throw new System.ArgumentException("Invalid value for i_FeedContents!");

            System.IO.StringReader StringReader = new System.IO.StringReader(i_FeedContents);
            System.Xml.Linq.XDocument XDocument = System.Xml.Linq.XDocument.Load(StringReader);

            this.Initialize
                (
                XDocument,
                i_ItemContainerXmlQualifiedName
                );
        }

        public FeedSyncFeed(System.Xml.Linq.XDocument i_XDocument, System.Xml.XmlQualifiedName i_ItemContainerXmlQualifiedName)
        {
            if (i_XDocument == null)
                throw new System.ArgumentException("i_XDocument must be non-null!");

            this.Initialize
                (
                i_XDocument,
                i_ItemContainerXmlQualifiedName
                );
        }

        protected void Initialize(System.Xml.Linq.XDocument i_XDocument, System.Xml.XmlQualifiedName i_ItemContainerXmlQualifiedName)
        {
            if (i_XDocument == null)
                throw new System.ArgumentException("i_XDocument must be non-null!");

            if (i_ItemContainerXmlQualifiedName == null)
                throw new System.ArgumentException("i_ItemContainerXmlQualifiedName must be non-null!");

            m_XDocument = i_XDocument;

            System.Xml.Linq.XNamespace XNamespace = i_ItemContainerXmlQualifiedName.Namespace;
            System.Xml.Linq.XName XName = XNamespace + i_ItemContainerXmlQualifiedName.Name;
            m_ItemContainerXElement = m_XDocument.Root.Element(XName);
            if (m_ItemContainerXElement == null)
                throw new System.Exception("No item container element found!");

            //  Get reference to 'sx:sharing' element
            XName = Microsoft.CSA.FeedSync.FeedSyncLibrarySL.Constants.FeedSyncXNamespace + Microsoft.CSA.FeedSync.FeedSyncLibrarySL.Constants.SHARING_ELEMENT_NAME;
            System.Xml.Linq.XElement SharingXElement = m_ItemContainerXElement.Element(XName);

            if (SharingXElement != null)
                m_Sharing = new Microsoft.CSA.FeedSync.FeedSyncLibrarySL.Sharing(SharingXElement);

            this.PopulateItems();
        }

        public System.Xml.Linq.XElement ItemContainerXElement
        {
            get
            {
                return m_ItemContainerXElement;
            }
        }

        public System.Xml.Linq.XDocument XDocument
        {
            get
            {
                return m_XDocument;
            }
        }

        public Microsoft.CSA.FeedSync.FeedSyncLibrarySL.Sharing Sharing
        {
            get
            {
                return m_Sharing;
            }
        }

        public System.Collections.Generic.IList<T> Items
        {
            get
            {
                return new System.Collections.Generic.List<T>(m_FeedSyncItemDictionary.Values);
            }
        }

        public T GetItem(string i_ID)
        {
            if (System.String.IsNullOrEmpty(i_ID))
                throw new System.ArgumentException("i_ID must be non-null!");

            return m_FeedSyncItemDictionary[i_ID];
        }

        public bool HasItem(string i_ID)
        {
            if (System.String.IsNullOrEmpty(i_ID))
                throw new System.ArgumentException("i_ID must be non-null!");

            return m_FeedSyncItemDictionary.ContainsKey(i_ID);
        }

        public bool FindItem(string i_ID, out T o_Item)
        {
            if (System.String.IsNullOrEmpty(i_ID))
                throw new System.ArgumentException("i_ID must be non-null!");

            o_Item = null;

            if (!this.HasItem(i_ID))
                return false;

            o_Item = m_FeedSyncItemDictionary[i_ID];
            return true;
        }

        public void AddItem(T i_FeedSyncItem)
        {
            if (i_FeedSyncItem == null)
                throw new System.Exception("i_FeedSyncItem must be non-null!");

            if (this.HasItem(i_FeedSyncItem.Sync.ID))
                throw new System.ArgumentException("Specified item already exists!");

            if (i_FeedSyncItem.XElement.Document != m_XDocument)
            {
                System.Xml.Linq.XElement ClonedXElement = new System.Xml.Linq.XElement(i_FeedSyncItem.XElement);

                i_FeedSyncItem = new T();
                i_FeedSyncItem.Initialize(ClonedXElement);
            }

            m_ItemContainerXElement.Add(i_FeedSyncItem.XElement);

            m_FeedSyncItemDictionary[i_FeedSyncItem.Sync.ID] = i_FeedSyncItem;
        }

        public void ReplaceItem(string i_ID, T i_FeedSyncItem)
        {
            if (System.String.IsNullOrEmpty(i_ID))
                throw new System.ArgumentException("i_ID must be non-null!");

            if (i_FeedSyncItem == null)
                throw new System.Exception("i_FeedSyncItem must be non-null!");

            T FeedSyncItem = null;

            if (!this.FindItem(i_ID, out FeedSyncItem))
                throw new System.ArgumentException("Specified item does not exist!");

            FeedSyncItem.XElement.Remove();

            if (i_FeedSyncItem.XElement.Document != m_XDocument)
            {
                System.Xml.Linq.XElement ClonedXElement = new System.Xml.Linq.XElement(i_FeedSyncItem.XElement);

                i_FeedSyncItem = new T();
                i_FeedSyncItem.Initialize(ClonedXElement);
            }

            m_ItemContainerXElement.Add(i_FeedSyncItem.XElement);

            m_FeedSyncItemDictionary[i_ID] = i_FeedSyncItem;
        }

        public System.Collections.Generic.IList<T> GetMergeChangeList(Microsoft.CSA.FeedSync.FeedSyncLibrarySL.FeedSyncFeed<T> i_IncomingFeed)
        {
            System.Collections.Generic.IList<T> MergedFeedSyncItemList = Microsoft.CSA.FeedSync.FeedSyncLibrarySL.Sync.Merge
                (
                this.Items,
                i_IncomingFeed.Items
                );

            return MergedFeedSyncItemList;
        }

        public void Merge(Microsoft.CSA.FeedSync.FeedSyncLibrarySL.FeedSyncFeed<T> i_IncomingFeed)
        {
            System.Collections.Generic.IList<T> MergedFeedSyncItemList = this.GetMergeChangeList(i_IncomingFeed);

            //  Merge feeds
            foreach (T MergedFeedSyncItem in MergedFeedSyncItemList)
            {
                if (this.HasItem(MergedFeedSyncItem.Sync.ID))
                {
                    this.ReplaceItem
                        (
                        MergedFeedSyncItem.Sync.ID,
                        MergedFeedSyncItem
                        );
                }
                else
                {
                    this.AddItem(MergedFeedSyncItem);
                }
            }
        }

        protected virtual void PopulateItems()
        {
            System.Xml.Linq.XName SyncXName = Microsoft.CSA.FeedSync.FeedSyncLibrarySL.Constants.FeedSyncXNamespace + Microsoft.CSA.FeedSync.FeedSyncLibrarySL.Constants.SYNC_ELEMENT_NAME;
            var ItemXElements =
                from ItemXElement in m_ItemContainerXElement.Elements()
                where (ItemXElement.Element(SyncXName) != null)
                select ItemXElement;

            //  Iterate items
            foreach (System.Xml.Linq.XElement ItemXElement in ItemXElements)
            {
                T FeedSyncItem = new T();
                FeedSyncItem.Initialize(ItemXElement);

                m_FeedSyncItemDictionary[FeedSyncItem.Sync.ID] = FeedSyncItem;
            }
        }
    }
}
